/*
 * Copyright (c) 2012, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 *  ======== Mmu.c ========
 */

#include <xdc/std.h>
#include <string.h>
#include <xdc/runtime/Assert.h>
#include <xdc/runtime/Startup.h>
#include <ti/sysbios/family/arm/arm9/Cache.h>
#include <ti/sysbios/hal/Hwi.h>

#include "package/internal/Mmu.xdc.h"

/*
 *  ======== Mmu_Module_startup ========
 */
Int Mmu_Module_startup(Int phase)
{
    /* disable the MMU */
    Mmu_disable();

    /* zero out the descriptor table entries */
    memset(Mmu_module->tableBuf, 0, 0x4000);
            
    /*  
     *  Initialize the descriptor table based upon static config
     *  This function is generated by the Mmu.xdt file.
     */
    Mmu_initTableBuf(Mmu_module);
    
    if (Mmu_enableMMU) {        
        /* enable the MMU */
        Mmu_enable();
    }
    
    return (Startup_DONE);
}

/*
 *  ======== Mmu_disable ========
 *  Function to disable the MMU.
 */
Void Mmu_disable()
{
    UInt16 type;
    UInt   key;

    /* if MMU is alreay disabled, just return */
    if (!(Mmu_isEnabled())) {
        return;
    }
    
    key = Hwi_disable();

    /* get the current enabled bits */
    type = Cache_getEnabled();
    
    if (type & Cache_Type_L1D) {
        /* writeback invalidate all data cache */ 
        Cache_wbInvAll();
        
        /* drain the write buffer */
        Cache_wait();
        
        /* disable the L1 data cache */
        Cache_disable(Cache_Type_L1D);
    }
    
    if (type & Cache_Type_L1P) {
        /* invalidate all L1 program cache */
        Cache_invL1pAll();

        /* disable L1P cache */
        Cache_disable(Cache_Type_L1P);
    }
    
    /* disables the MMU */
    Mmu_disableAsm();
    
    /* set cache back to initial settings */
    Cache_enable(type);

    Hwi_restore(key);
}

/*
 *  ======== Mmu_enable ========
 *  Function to enable the MMU.
 */
Void Mmu_enable()
{
    UInt16 type;
    UInt   key;

    /* if MMU is already enabled then just return */
    if (Mmu_isEnabled()) {
        return;
    }

    key = Hwi_disable();

    /* get the current enabled bits */
    type = Cache_getEnabled();
    
    if (type & Cache_Type_L1P) {
        /* invalidate all of L1 program cache */
        Cache_invL1pAll();

        /* disable L1P cache */
        Cache_disable(Cache_Type_L1P);
    }
    
    /* enables the MMU */
    Mmu_enableAsm();
    
    /* set cache back to initial settings */
    Cache_enable(type);

    Hwi_restore(key);
}

/*
 *  ======== Mmu_initDescAttrs ========
 */
Void Mmu_initDescAttrs(Mmu_FirstLevelDescAttrs *attrs)
{
    /* Assert that attrs != NULL */
    Assert_isTrue(attrs != NULL, Mmu_A_nullPointer);
    
    attrs->type = Mmu_defaultAttrs.type;
    attrs->bufferable = Mmu_defaultAttrs.bufferable;        
    attrs->cacheable = Mmu_defaultAttrs.cacheable;
    attrs->imp = Mmu_defaultAttrs.imp;
    attrs->domain = Mmu_defaultAttrs.domain;
    attrs->accPerm = Mmu_defaultAttrs.accPerm;
    attrs->imp = Mmu_defaultAttrs.imp;
}

/*
 *  ======== Mmu_setFirstLevelDesc ========
 */
Void Mmu_setFirstLevelDesc(Ptr virtualAddr, Ptr phyAddr,
                           Mmu_FirstLevelDescAttrs *attrs)
{
    UInt32 index = (UInt32)virtualAddr >> 20;
    UInt32 desc = 0;
    Bool   enabled;
    
    /* Assert that attrs != NULL */
    Assert_isTrue(attrs != NULL, Mmu_A_nullPointer);
    
    /* determine the current state of the MMU */
    enabled = Mmu_isEnabled();

    /* disable the MMU (if already disabled, does nothing) */
    Mmu_disable();

    /* Determine which kind of descriptor. */
    switch (attrs->type) {
        case Mmu_FirstLevelDesc_COARSE:
            /* Coarse page table descriptor */
            desc = (attrs->type) |
                   (attrs->imp << 4) |
                   (attrs->domain << 5) |
                   ((UInt32)phyAddr & 0xFFFFFC00);
            break;
            
        /* Section descriptor */
        case Mmu_FirstLevelDesc_SECTION:
            desc = (attrs->type) |
                   (attrs->bufferable << 2) |
                   (attrs->cacheable << 3) |
                   (attrs->imp << 4) |
                   (attrs->domain << 5) |
                   (attrs->accPerm << 10) |
                   ((UInt32)phyAddr & 0xFFF00000);
            break;
            
        /* Fine page table descriptor */
        case Mmu_FirstLevelDesc_FINE:
            desc = (attrs->type) |
                   (attrs->imp << 4) |
                   (attrs->domain << 5) |
                   ((UInt32)phyAddr & 0xFFFF0000);
            break;
        
        default:
            Assert_isTrue(FALSE, Mmu_A_unknownDescType);
            break;
    }
    
    /* set the entry in the first level descriptor table */
    Mmu_module->tableBuf[index] = desc;

    if (enabled) {
        /* if MMU was enabled, then re-enable it */
        Mmu_enable();
    }
}
